using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Xna.Framework;

using GarageGames.Torque.Core;
using GarageGames.Torque.Util;
using GarageGames.Torque.Sim;
using GarageGames.Torque.T2D;
using GarageGames.Torque.SceneGraph;
using GarageGames.Torque.MathUtil;
using Microsoft.Xna.Framework.Input;
using Xyne.Templates;
using Xyne.Templates.PuzzlePieceTemplate;

namespace Xyne.Components
{
    [TorqueXmlSchemaType]
    public class PuzzleMove : TorqueComponent, ITickObject
    {
        private const float SlideThreshold = 0.1f;

        private PlayerPieces sceneObject;
        private float slideCounter;
        private float slideAmount;
        private int puzzlePieceWidth;
        private Vector2 testPos;

        public PlayerPieces SceneObject
        {
            get { return sceneObject as PlayerPieces; }
        }

        public int PuzzlePieceWidth
        {
            get { return puzzlePieceWidth; }
            set { puzzlePieceWidth = value; }
        }


        public PuzzleMove()
        {
            slideCounter = 0;
        }

        public virtual void ProcessTick(Move move, float dt)
        {
                slideCounter += dt * slideAmount;

                float slide = 0;
                if (slideCounter >= SlideThreshold)
                {
                    slideCounter = 0;
                    slide = puzzlePieceWidth; // +1;
                }
                if (slideCounter <= -SlideThreshold)
                {
                    slideCounter = 0;
                    slide = -puzzlePieceWidth; // -1;
                }
                if (slide != 0)
                {
                    if (CheckSlideCollision(slide))
                    {
                        sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X + slide, sceneObject.Position.Y), 0);
                    }
                }
            
        }

        public virtual void InterpolateTick(float k)
        {
        }

        public override void CopyTo(TorqueComponent obj)
        {
            PuzzleMove move = obj as PuzzleMove;
            move.PuzzlePieceWidth = PuzzlePieceWidth;

            base.CopyTo(move);
        }

        protected override bool _OnRegister(TorqueObject owner)
        {
            if (!base._OnRegister(owner) || !(owner is PlayerPieces))
                return false;

            sceneObject = owner as PlayerPieces;

            ProcessList.Instance.AddTickCallback(Owner, this, 0);

            return true;
        }

        protected override void _OnUnregister()
        {
            base._OnUnregister();
        }

        protected override void _RegisterInterfaces(TorqueObject owner)
        {
            base._RegisterInterfaces(owner);
        }

        public void OnRight(float val)
        {
            slideAmount = val;

            if (val == 0)
            {
                slideCounter = SlideThreshold;
            }
        }

        public void OnLeft(float val)
        {
            slideAmount = -val;

            if (val == 0)
            {
                slideCounter = -SlideThreshold;
            }
        }

        public void OnDown(float val)
        {
            if (val == 0)
            {
                sceneObject.Physics.VelocityY = 50;
            }
            else
            {
                sceneObject.Physics.VelocityY = 1000;
            }
        }

        public void OnRotate(float val)
        {
            if (val == 1)
            {
                if (CheckRotationCollision(false))
                    AdvanceRotation(false);
            }
        }

        public void OnReverseRotate(float val)
        {
            if (val == 1)
            {
                if (CheckRotationCollision(true))
                    AdvanceRotation(true);
            }
        }

        private bool CheckSlideCollision(float slideAmount)
        {
            if (slideAmount == 0) return false;

            if (slideAmount < 0)
            {
                float pos = Math.Min(sceneObject.First.Position.X, sceneObject.Second.Position.X) + slideAmount;

                if (pos <= sceneObject.WorldLimit.MoveLimitMin.X)
                    return false;
            }
            else if (slideAmount > 0)
            {
                float pos = Math.Max(sceneObject.First.Position.X, sceneObject.Second.Position.X) + slideAmount;

                if (pos >= sceneObject.WorldLimit.MoveLimitMax.X)
                    return false;
            }

            List<PuzzlePiece> pieces = new List<PuzzlePiece>();
            TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>(ref pieces);
            
            foreach (PuzzlePiece piece in pieces)
            {
                testPos = sceneObject.First.Position;
                testPos.X += slideAmount;
                if (piece.Position.X == testPos.X && piece != sceneObject.Second && 
                    (testPos.Y == piece.Position.Y ||
                    (testPos.Y < piece.Position.Y && testPos.Y + (sceneObject.First.Size.Y / 2) > piece.Position.Y - (piece.Size.Y / 2)) ||
                    (testPos.Y > piece.Position.Y && testPos.Y - (sceneObject.First.Size.Y / 2) < piece.Position.Y + (piece.Size.Y / 2))))
                {
                    return false;
                }

                testPos = sceneObject.Second.Position;
                testPos.X += slideAmount;
                if (piece.Position.X == testPos.X && piece != sceneObject.First && 
                    (testPos.Y == piece.Position.Y ||
                    (testPos.Y < piece.Position.Y && testPos.Y + (sceneObject.First.Size.Y / 2) > piece.Position.Y - (piece.Size.Y / 2)) ||
                    (testPos.Y > piece.Position.Y && testPos.Y - (sceneObject.First.Size.Y / 2) < piece.Position.Y + (piece.Size.Y / 2))))
                {
                    return false;
                }
            }

            return true;
        }

        private bool CheckRotationCollision(bool reverse)
        {
            testPos = sceneObject.First.Position;
            if ((sceneObject.Second.Position.X > sceneObject.First.Position.X && !reverse) ||
                (sceneObject.Second.Position.X < sceneObject.First.Position.X && reverse))
            {
                testPos.Y += puzzlePieceWidth;
            }
            else if ((sceneObject.Second.Position.Y > sceneObject.First.Position.Y && !reverse) ||
                     (sceneObject.Second.Position.Y < sceneObject.First.Position.Y && reverse))
            {
                testPos.X += -puzzlePieceWidth;
            }
            else if ((sceneObject.Second.Position.X < sceneObject.First.Position.X && !reverse) ||
                     (sceneObject.Second.Position.X > sceneObject.First.Position.X && reverse))
            {
                testPos.Y += -puzzlePieceWidth;
            }
            else if ((sceneObject.Second.Position.Y < sceneObject.First.Position.Y && !reverse) ||
                     (sceneObject.Second.Position.Y > sceneObject.First.Position.Y && reverse))
            {
                testPos.X += puzzlePieceWidth;
            }

            bool collide = false;
            if (testPos.X <= sceneObject.WorldLimit.MoveLimitMin.X ||
                testPos.X >= sceneObject.WorldLimit.MoveLimitMax.X ||
                testPos.Y <= sceneObject.WorldLimit.MoveLimitMin.Y ||
                testPos.Y >= sceneObject.WorldLimit.MoveLimitMax.Y)
            {
                collide = true;
            }
            else
            {
                List<PuzzlePiece> pieces = new List<PuzzlePiece>();
                TorqueObjectDatabase.Instance.FindRegisteredObjects<PuzzlePiece>(ref pieces);

                foreach (PuzzlePiece piece in pieces)
                {
                    if (piece.Position.X == testPos.X &&
                        (testPos.Y == piece.Position.Y ||
                        (testPos.Y < piece.Position.Y && testPos.Y + (sceneObject.Second.Size.Y / 2) > piece.Position.Y - (piece.Size.Y / 2)) ||
                        (testPos.Y > piece.Position.Y && testPos.Y - (sceneObject.Second.Size.Y / 2) < piece.Position.Y + (piece.Size.Y / 2))))
                    {
                        collide = true;
                    }
                }
            }

            return !collide;
        }

        private void AdvanceRotation(bool reverse)
        {
            if ((sceneObject.Second.Position.X > sceneObject.First.Position.X && !reverse) ||
                (sceneObject.Second.Position.X < sceneObject.First.Position.X && reverse))
            {
                if (reverse)
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y + (sceneObject.First.Size.X / 2)), 0);
                }
                else
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y + (sceneObject.First.Size.X / 2)), 0);
                }
                sceneObject.Size = new Vector2(sceneObject.Size.Y, sceneObject.Size.X);
                sceneObject.First.WarpToPosition(new Vector2(sceneObject.Position.X, sceneObject.Position.Y - (sceneObject.First.Size.Y / 2)), 0);
                sceneObject.First.Mount(sceneObject, sceneObject.FirstLinkPointNames[1], true);
                sceneObject.Second.WarpToPosition(new Vector2(sceneObject.Position.X, sceneObject.Position.Y + (sceneObject.Second.Size.Y / 2)), 0);
                sceneObject.Second.Mount(sceneObject, sceneObject.SecondLinkPointNames[1], true);
            }
            else if ((sceneObject.Second.Position.Y > sceneObject.First.Position.Y && !reverse) ||
                     (sceneObject.Second.Position.Y < sceneObject.First.Position.Y && reverse))
            {
                if (reverse)
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y + (sceneObject.First.Size.X / 2) + 1), 0);
                }
                else
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y - (sceneObject.First.Size.X / 2)), 0);
                }
                sceneObject.Size = new Vector2(sceneObject.Size.Y, sceneObject.Size.X);
                sceneObject.First.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2), sceneObject.Position.Y), 0);
                sceneObject.First.Mount(sceneObject, sceneObject.FirstLinkPointNames[2], true);
                sceneObject.Second.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.Second.Size.X / 2), sceneObject.Position.Y), 0);
                sceneObject.Second.Mount(sceneObject, sceneObject.SecondLinkPointNames[2], true);
            }
            else if ((sceneObject.Second.Position.X < sceneObject.First.Position.X && !reverse) ||
                     (sceneObject.Second.Position.X > sceneObject.First.Position.X && reverse))
            {
                if (reverse)
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y - (sceneObject.First.Size.X / 2)), 0);
                }
                else
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y - (sceneObject.First.Size.X / 2)), 0);
                }
                sceneObject.Size = new Vector2(sceneObject.Size.Y, sceneObject.Size.X);
                sceneObject.First.WarpToPosition(new Vector2(sceneObject.Position.X, sceneObject.Position.Y + (sceneObject.Second.Size.Y / 2)), 0);
                sceneObject.First.Mount(sceneObject, sceneObject.FirstLinkPointNames[3], true);
                sceneObject.Second.WarpToPosition(new Vector2(sceneObject.Position.X, sceneObject.Position.Y - (sceneObject.First.Size.Y / 2)), 0);
                sceneObject.Second.Mount(sceneObject, sceneObject.SecondLinkPointNames[3], true);
            }
            else if ((sceneObject.Second.Position.Y < sceneObject.First.Position.Y && !reverse) ||
                     (sceneObject.Second.Position.Y > sceneObject.First.Position.Y && reverse))
            {
                if (reverse)
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y - (sceneObject.First.Size.X / 2)), 0);
                }
                else
                {
                    sceneObject.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2),
                                   sceneObject.Position.Y + (sceneObject.First.Size.X / 2) + 1), 0);
                }
                sceneObject.Size = new Vector2(sceneObject.Size.Y, sceneObject.Size.X);
                sceneObject.First.WarpToPosition(new Vector2(sceneObject.Position.X - (sceneObject.Second.Size.X / 2), sceneObject.Position.Y), 0);
                sceneObject.First.Mount(sceneObject, sceneObject.FirstLinkPointNames[0], true);
                sceneObject.Second.WarpToPosition(new Vector2(sceneObject.Position.X + (sceneObject.First.Size.X / 2), sceneObject.Position.Y), 0);
                sceneObject.Second.Mount(sceneObject, sceneObject.SecondLinkPointNames[0], true);
            }
        }
    }
}
